﻿using System.IO;
using System.Linq;
using GameViewModel;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace GameTests
{
    [TestClass]
    public class TheGameTest
    {
        [TestMethod]
        public void CreateGame()
        {
            var game = new TheGame();
            Assert.IsNotNull(game);
        }

        [TestMethod]
        public void Persons()
        {
            var game = new TheGame();
            var person1 = new Person("Гостомысл", 1);
            var person2 = new Person("Афиноген", 2);
            game.Options.Persons.Add(person1);
            game.Options.Persons.Add(person2);
            Assert.IsTrue(game.Options.Persons.Count == 2);

            game.Options.Persons.Remove(person2);
            Assert.IsTrue(game.Options.Persons.Count == 1);
        }

        [TestMethod]
        [ExpectedException(typeof(InvalidDataException))]
        public void WrongPlayersStartGame()
        {
            var game = new TheGame();
            var person1 = new Person("Евлампий", 1);
            var person2 = new Person("Сигизмунд", 2);
            var person3 = new Person("Ратибор", 3);
            game.Options.Persons.Add(person1);
            game.Options.Persons.Add(person2);
            game.Options.Persons.Add(person3);
            Assert.IsTrue(game.Options.Persons.Count == 3);

            person1.IsSelected = true;
            game.Start();
        }

        [TestMethod]
        public void StartGameNormalOrder()
        {
            var game = new TheGame();
            var person1 = new Person("Евлампий", 1) { IsSelected = true };
            var person2 = new Person("Сигизмунд", 2) { IsSelected = true };
            var person3 = new Person("Ратибор", 3) { IsSelected = false };
            game.Options.Persons.Add(person1);
            game.Options.Persons.Add(person2);
            game.Options.Persons.Add(person3);
            Assert.IsTrue(game.Options.Persons.Count == 3);

            game.Start();
            Assert.IsTrue(game.Players.Count == 2);
            var first = game.Players.First;
            Assert.IsTrue(first.Value.Name == person1.Name);
            var second = first.NextOrFirst();
            Assert.IsTrue(second.Value.Name == person2.Name);
        }

        [TestMethod]
        public void StartGameRandomOrder()
        {
            var game = new TheGame();
            for (var i = 0; i < 100; i++)
                game.Options.Persons.Add(new Person("Игрок" + i, i) { IsSelected = true });
            Assert.IsTrue(game.Options.Persons.Count == 100);
            game.Options.IsRandomOrder = true;
            game.Start();
            Assert.IsTrue(game.Players.Count == 100);
            var selectedPersons = game.Options.Persons.Where(p => p.IsSelected).AsString();
            var players = game.Players.AsString();
            Assert.AreNotEqual(selectedPersons, players);
        }


        [TestMethod]
        public void StartGameReversed()
        {
            var game = new TheGame();
            var person1 = new Person("Евлампий", 1) { IsSelected = true };
            var person2 = new Person("Сигизмунд", 2) { IsSelected = true };
            var person3 = new Person("Ратибор", 3) { IsSelected = false };
            game.Options.Persons.Add(person1);
            game.Options.Persons.Add(person2);
            game.Options.Persons.Add(person3);
            Assert.IsTrue(game.Options.Persons.Count == 3);

            game.Start();
            Assert.IsTrue(game.Players.Count == 2);
            var first = game.Players.First;
            Assert.IsTrue(first.Value.Name == person1.Name);
            var second = first.NextOrFirst();
            Assert.IsTrue(second.Value.Name == person2.Name);

            game.StartReversed();
            Assert.IsTrue(game.Players.Count == 2);
            first = game.Players.First;
            Assert.IsTrue(first.Value.Name == person2.Name);
            Assert.IsTrue(first.Value.Order == 1);
            second = first.NextOrFirst();
            Assert.IsTrue(second.Value.Name == person1.Name);
            Assert.IsTrue(second.Value.Order == 2);
        }

        [TestMethod]
        public void PlayingRoutine()
        {
            var game = new TheGame();
            var person1 = new Person("Евлампий", 1) { IsSelected = true };
            var person2 = new Person("Сигизмунд", 2) { IsSelected = true };
            var person3 = new Person("Ратибор", 3) { IsSelected = true };
            var person4 = new Person("Гостомысл", 4) { IsSelected = false };
            game.Options.Persons.Add(person1);
            game.Options.Persons.Add(person2);
            game.Options.Persons.Add(person3);
            game.Options.Persons.Add(person4);
            Assert.IsTrue(game.Options.Persons.Count == 4);

            game.Start();
            Assert.IsTrue(game.Players.Count == 3);
            var first = game.Players.First;
            var second = first.NextOrFirst();
            var third = second.NextOrFirst();
            Assert.AreEqual(first.Value.Name, person1.Name);
            Assert.AreEqual(second.Value.Name, person2.Name);
            Assert.AreEqual(third.Value.Name, person3.Name);
            Assert.AreEqual(first, third.NextOrFirst());
            Assert.AreEqual(third, first.PreviousOrLast());

            game.Plus(first.Value);
            //bonus:0 players: 1 0 -1
            Assert.IsTrue(CheckGameState(game, 0, new[] { 1, 0, -1 }));

            game.Plus(first.Value);
            //bonus:0 players: 2 0 -2
            Assert.IsTrue(CheckGameState(game, 0, new[] { 2, 0, -2 }));

            game.Penalty(first.Value);
            //bonus:1 players: 1 0 -2
            Assert.IsTrue(CheckGameState(game, 1, new[] { 1, 0, -2 }));

            game.Penalty(first.Value);
            //bonus:2 players: 0 0 -2
            Assert.IsTrue(CheckGameState(game, 2, new[] { 0, 0, -2 }));

            game.Plus(second.Value);
            //bonus:0 players: -1 3 -2
            Assert.IsTrue(CheckGameState(game, 0, new[] { -1, 3, -2 }));

            game.Minus(second.Value);
            //bonus:0 players: 0 2 -2
            Assert.IsTrue(CheckGameState(game, 0, new[] { 0, 2, -2 }));

            var total = game.Players.Sum(p => p.PlayerScore) + game.BonusScore;
            Assert.AreEqual(0, total);
        }

        [TestMethod]
        public void PlayingRoutineUnit()
        {
            var game = new TheGame();
            game.Options.Unit = 9250;
            var person1 = new Person("Евлампий", 1) { IsSelected = true };
            var person2 = new Person("Сигизмунд", 2) { IsSelected = true };
            var person3 = new Person("Ратибор", 3) { IsSelected = true };
            game.Options.Persons.Add(person1);
            game.Options.Persons.Add(person2);
            game.Options.Persons.Add(person3);
            Assert.IsTrue(game.Options.Persons.Count == 3);

            game.Start();
            var first = game.Players.First;
            var second = first.NextOrFirst();
            var third = second.NextOrFirst();
            Assert.AreEqual(first.Value.Name, person1.Name);
            Assert.AreEqual(second.Value.Name, person2.Name);
            Assert.AreEqual(third.Value.Name, person3.Name);
            Assert.AreEqual(first, third.NextOrFirst());
            Assert.AreEqual(third, first.PreviousOrLast());

            game.Plus(first.Value);
            //bonus:0 players: 9250 0 -9250
            Assert.IsTrue(CheckGameState(game, 0, new[] { 9250, 0, -9250 }));

            game.Plus(first.Value);
            //bonus:0 players: 18500 0 -18500
            Assert.IsTrue(CheckGameState(game, 0, new[] { 18500, 0, -18500 }));

            game.Penalty(first.Value);
            //bonus:9250 players: 9250 0 -18500
            Assert.IsTrue(CheckGameState(game, 9250, new[] { 9250, 0, -18500 }));

            game.Penalty(first.Value);
            //bonus:18500 players: 0 0 -18500
            Assert.IsTrue(CheckGameState(game, 18500, new[] { 0, 0, -18500 }));

            game.Plus(second.Value);
            //bonus:0 players: -9250 27750 -18500
            Assert.IsTrue(CheckGameState(game, 0, new[] { -9250, 27750, -18500 }));

            game.Minus(second.Value);
            //bonus:0 players: 0 18500 -18500
            Assert.IsTrue(CheckGameState(game, 0, new[] { 0, 18500, -18500 }));

            var total = game.Players.Sum(p => p.PlayerScore) + game.BonusScore;
            Assert.AreEqual(0, total);
        }

        public bool CheckGameState(TheGame game, int expectedBonus, int[] expectedPlayerPoints)
        {
            if (game.Players.Count != expectedPlayerPoints.Length) return false;
            var gameScoreValid = game.BonusScore == expectedBonus;
            var gamePoints = game.Players.Select(p => p.PlayerScore).ToArray();
            var playersPointsValid = gamePoints.AsString() == expectedPlayerPoints.AsString();
            return gameScoreValid && playersPointsValid;
        }
    }
}
